package dbms.query;

import java.util.ArrayList;
import java.util.List;

import dbms.Debug;
import dbms.absyn.*;

public class PlanTree {
	private Plan root = new Plan();
	private List<PlanTree> child = new ArrayList<PlanTree>();
	private boolean isLeaf = false;
	private ResultSet resultSet;
	
	public PlanTree(Plan rootPlan) {
		this.setRoot(rootPlan);
	}

	public PlanTree() {
	}
	
	public void query(SelectExp selectExp) {
		PlanTree planTree = this;
		PlanTree position = planTree;

		SelectClause selectClause = selectExp.select_stmt;	
		FromClause fromClause = selectExp.from_stmt;
		WhereClause whereClause = selectExp.where_stmt;
		//OrderClause orderClause = selectExp.order_stmt;
		
		
		//Step 1: add the projection plan
		//get selectList
		if (selectClause.str != "TIMES") {
			List<String> selectList = new ArrayList<String>();
			AttrList attrList = selectClause.attr_list;
			while (attrList != null) {
				selectList.add(attrList.attr.attr.name);
				attrList = attrList.next;
			}
			//add plan: projection
			position.setRoot(new ProjectPlan(selectList));
			//set planTree to the first layer
			position.addChild(new PlanTree());
			position = position.getChild().get(0);
		}
		
		
		//Step 2: add the selection plan
		//get ConditionList
		ConditionList cl;
		if (whereClause != null) {
			cl = whereClause.condition_list;
			if (cl.condition.type != 5) {
				//add plan: selection
				position.setRoot(new SelectPlan(cl));
				//set planTree to the second layer
				position.addChild(new PlanTree());
				position = position.getChild().get(0);
			}
			else {
				PlanTree subQuery = new PlanTree();
				subQuery.query(cl.condition.exp);
				Debug.P("SubQuery");
				subQuery.print();
				String attr = cl.condition.exp.select_stmt.attr_list.attr.attr.name;
				ResultSet rs = subQuery.execute();
				rs.resultName = attr;
				position.setRoot(new SelectPlan(subQuery.execute()));
				position.addChild(new PlanTree());
				position = position.getChild().get(0);
			}
		}
		/*
		while (clist != null) {
			//add condition in ConditionList and add a new PlanTree when the condition is a subQuery 
			if (clist.condition.type == 5) {
				
				Debug.P("SubQuery");
				planTree.getChild().add(subQuery);
				clist.next = clist.next.next;
			}
			clist = clist.next;
		}*/
		
		
		//Step 3: add the production plan and the table plan
		//get the tableList
		List<String> tableList = new ArrayList<String>();
		AttrList table_list = fromClause.tableList;
		while (table_list != null) {
			tableList.add(table_list.attr.attr.name);
			table_list = table_list.next;
		}
		while (tableList.size() > 0) {
			String tableName = tableList.remove(0);
			if (tableList.size() == 0) {
				position.setRoot(new TablePlan(tableName));
				position.setLeaf(true);
			}
			else {
				position.setRoot(new ProductPlan());
				position.addChild(new PlanTree(new TablePlan(tableName)));
				position.getChild().get(0).setLeaf(true);
				position.addChild(new PlanTree());
				position = position.getChild().get(1);
			}
		}
	}
	
	public void print() {
		if (root instanceof ProjectPlan) Debug.P("ProjectPlan");
		if (root instanceof SelectPlan) Debug.P("SelectPlan");
		if (root instanceof ProductPlan) Debug.P("ProductPlan");
		if (root instanceof TablePlan) Debug.P("TablePlan");
		if (root == null) Debug.P("NULL");
		if (child.size() > 0) child.get(0).print();
	}

	
	//execute the PlanTree to get the final ResultSet
	public ResultSet execute() {
		PlanTree pt = this;
		while (true) {
			Plan r = pt.root;
			if (r instanceof TablePlan) {
				return ((TablePlan) r).execute();
			}
			PlanTree temp = pt.getChild().get(0);
			ResultSet old = temp.execute();
			if (r instanceof ProjectPlan) {
				return ((ProjectPlan) r).execute(old);
			}
			if (r instanceof SelectPlan) {
				return ((SelectPlan) r).execute(old);
			}
			if (r instanceof ProductPlan) {
				ResultSet old2 = pt.getChild().get(1).execute();
				return ((ProductPlan) r).execute(old, old2);
			}
		}
	}
	
	public void addChild (PlanTree planTree) {
		this.getChild().add(planTree);
	}

	public void setRoot(Plan root) {
		this.root = root;
	}

	public Plan getRoot() {
		return root;
	}

	public void setLeaf(boolean isLeaf) {
		this.isLeaf = isLeaf;
	}

	public boolean isLeaf() {
		return isLeaf;
	}

	public void setResultSet(ResultSet resultSet) {
		this.resultSet = resultSet;
	}

	public ResultSet getResultSet() {
		return resultSet;
	}

	public void setChild(List<PlanTree> child) {
		this.child = child;
	}

	public List<PlanTree> getChild() {
		return child;
	}
}
